/*
 *  Copyright (C) 2010 Glauco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.annotation.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 *
 * @author Glauco
 */
public class ReflectUtilities {

    Class clazz;

    public ReflectUtilities(Class clazz) {
        this.clazz = clazz;
    }

    public ReflectUtilities(Object obj) {
        this(obj.getClass());
    }

    /**
     * Lista todos os campos da classe e das superclasses.
     * @return
     */
    public List<Field> getDeclaredFields(){

        List fields = new ArrayList();

        fields.addAll(Arrays.asList(clazz.getDeclaredFields()));

        Class superClass = clazz.getSuperclass();

        if(superClass != null && !superClass.equals(Object.class)){
            ReflectUtilities ru = new ReflectUtilities(superClass);
            fields.addAll(ru.getDeclaredFields());
        }

        return fields;
    }

    /**
     * Pega o Field da classe pela propriedade.
     * Retorna null se não encontrar ou ocorrer alguma erro.
     *
     * @param property
     * @return
     */
    public Field getField(String property){
        return getField(clazz, property);
    }

    private Field getField(Class c, String property){
        for (Field f : c.getDeclaredFields()) {
            if(f.getName().equals(property))
                return f;
        }

        Class superClass = c.getSuperclass();
        if (!superClass.equals(Object.class))
            return getField(superClass, property);

        return null;
    }

    /**
     * Retorna a Classe que representa o tipo do Field da propriedade.
     * Retorna null se não encontrar ou ocorrer alguma erro.
     *
     * @param property
     * @return
     */
    public Class getFieldType(String property){
        try{
            Field field = getField(property);
            return field.getType();
        }catch(Exception ex){
            //System.out.println(ex.getMessage());
            return null;
        }
    }

    /**
     * Pega o Método get para o Field da propriedade.
     * Retorna null se não encontrar ou ocorrer alguma erro.
     *
     * @param property
     * @return
     */
    public Method getGetMethod(String property){
        try{
            String get = "get" + property.substring(0, 1).toUpperCase() +
                    property.substring(1);
            return clazz.getMethod(get);
        }catch(Exception ex){
            //System.out.println(ex.getMessage());
            return null;
        }
    }

    /**
     * Pega o método set para Field da propriedade.
     * Retorna null se não encontrar ou ocorrer alguma erro.
     * 
     * @param property
     * @return
     */
    public Method getSetMethod(String property){
        try{
            String set = "set" + property.substring(0, 1).toUpperCase() +
                    property.substring(1);
            return clazz.getMethod(set, getFieldType(property));
        }catch(Exception ex){
            //System.out.println(ex.getMessage());
            return null;
        }
    }

    /**
     * Retorna um Método igualmente a chamar <code>getMethod</code> do um
     * objeto de <code>Class</code>.
     * @param method
     * @param parameterTypes
     * @return
     */
    public Method getMethod(String method, Class<?>... parameterTypes){
        try{
            return clazz.getMethod(method, parameterTypes);
        }catch(Exception ex){
            //System.out.println(ex.getMessage());
            return null;
        }
    }

    public List<Method> getDeclaredMethods(){

        List<Method> methods = new ArrayList<Method>();

        methods.addAll(Arrays.asList(clazz.getMethods()));

        return methods;
    }
}
